package org.je.xml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.je.util.StringUtil;

/***
 * 元素
 * 
 * @author fan yi feng
 */
public class Element
{
	int					offset;
	List<Element>		children;
	byte				type;
	String				value;
	Map<String, String>	atts;
	String				id;

	Element() {}

	public boolean isNode() {
		return type == XML_CLOSED_NODE;
	}

	public boolean isText() {
		return type == XML_TEXT;
	}

	public boolean isCDATA() {
		return type == XML_CDATA;
	}

	public boolean isNotes() {
		return type == XML_NOTES;
	}

	public int size() {
		return children == null ? 0 : children.size();
	}

	/**
	 * @param ele
	 */
	public boolean delete(Element ele) {
		if (children == null)
			return false;
		for (int i = 0; i < children.size(); ++i) {
			if (ele == children.get(i)) {
				children.remove(i);
				return true;
			}
		}
		return false;
	}

	/**
	 * @param tagName
	 */
	public static Element createNode(String tagName) {
		if (!XmlAttrUtil.isNameStr(tagName))
			throw new ParseXmlException("[" + tagName + "]is not a tag name str.");
		Element ele = new Element();
		ele.type = XML_CLOSED_NODE;
		ele.value = tagName;
		return ele;
	}

	/**
	 * @param tagName
	 */
	public Element addNode(String tagName) {
		Element temp = createNode(tagName);
		add(temp);
		return temp;
	}

	/**
	 * @param str
	 */
	public Element addText(String str) {
		return addStr(str ,"<" ,XML_TEXT);
	}

	/**
	 * @param str
	 */
	public Element addCDATA(String str) {
		return addStr(str ,"]]>" ,XML_CDATA);
	}

	/**
	 * @param str
	 */
	public Element addNOTES(String str) {
		return addStr(str ,"-->" ,XML_NOTES);
	}

	public String getTagName() {
		return type == XML_CLOSED_NODE ? value : null;
	}

	public Map<String, String> getAttrs() {
		return atts == null ? new HashMap<String, String>() : new HashMap<String, String>(atts);
	}

	/**
	 * @param name
	 */
	public String getAtt(String name) {
		return atts == null ? null : atts.get(name);
	}

	public Element getFirst() {
		return size() == 0 ? null : children.get(0);
	}

	public Element getLast() {
		return size() == 0 ? null : children.get(children.size() - 1);
	}

	/**
	 * @param tagName
	 */
	public Element getFirst(String tagName) {
		if (children == null)
			return null;
		for (Element e : children) {
			if (e.type == Element.XML_CLOSED_NODE) {
				if (e.value.equals(tagName))
					return e;
			}
		}
		return null;
	}

	public Element getFirstNode() {
		if (children == null)
			return null;
		for (Element e : children) {
			if (e.type == Element.XML_CLOSED_NODE)
				return e;
		}
		return null;
	}

	public List<Element> getChildren() {
		return children == null ? new ArrayList<Element>() : new ArrayList<Element>(children);
	}

	/**
	 * @param idx
	 * @return
	 */
	public Element getChild(int idx) {
		return children.get(idx);
	}

	/**
	 * @param tagName
	 */
	public List<Element> getElementsByTagName(String tagName) {
		if (children == null)
			return null;
		List<Element> list = new ArrayList<Element>();
		for (Element e : children) {
			if (e.type == Element.XML_CLOSED_NODE) {
				if (e.value.equals(tagName))
					list.add(e);
			}
		}
		return list;
	}

	/**
	 * @param id
	 */
	public Element getElementById(String id) {
		return XmlEleUtil.getElementById(this ,id);
	}

	public List<Element> getChildNodes() {
		if (children == null)
			return null;
		List<Element> list = new ArrayList<Element>();
		for (Element e : children) {
			if (e.type == Element.XML_CLOSED_NODE)
				list.add(e);
		}
		return list;
	}

	public String getValue() {
		if (type == XML_TEXT || type == XML_CDATA || type == XML_NOTES)
			return value;
		throw new ParseXmlException("type[" + type + "] not support this method.");
	}

	public Element setAtt(String name, String value) {
		if (!XmlAttrUtil.isNameStr(name))
			throw new ParseXmlException("illegal-attribue-name: " + name);

		value = value == null ? "" : value;
		if (value.indexOf("'") != -1 && value.indexOf("\"") != -1)
			value = value.replaceAll("\"" ,"'");

		addAtt(name ,value);
		return this;
	}

	public String toString() {
		return XmlEleUtil.toXml(this ,0);
	}

	// private method
	void add(Element ele) {
		if (children == null)
			children = new ArrayList<Element>();
		children.add(ele);
	}

	public void addAtt(String name, String value) {
		if (atts == null)
			atts = new HashMap<String, String>(8);
		if ("id".equalsIgnoreCase(name))
			id = value;
		atts.put(name ,value);
	}

	private Element addStr(String str, String token, byte t) {
		if (!StringUtil.isBlank(str)) {
			if (str.indexOf(token) >= 0)
				throw new RuntimeException("[" + str + "] contains some illegal char.");
			Element ele = new Element();
			ele.type = t;
			ele.value = str.trim();
			add(ele);//
			return ele;
		}
		return null;
	}

	public static final byte	XML_CLOSED_NODE	= 1;	// 关闭好的节点
	public static final byte	XML_TEXT		= 2;	// 文本
	public static final byte	XML_CDATA		= 3;	// CDATA文本
	public static final byte	XML_NOTES		= 4;	// 注释
	public static final byte	TAG_START		= 5;	// 开始标签
	public static final byte	TAG_END			= 6;	// 结束标签
}
